// 泛型就是泛指的类型，在定义函数、类和接口的时候，先不指定类型，等到运行时再指定某一类型
// 使用泛型有一个好处，就是可以减少使用any这种不确定的类型
// 定义泛型的时候我们常用T表示某种未知的类型，使用T只是一个约定，事实上可以使用你想用的字符
function createArray<T>(length: number, value: T): Array<T> {
  let res: T[] = [];
  for(let i = 0; i < length; i++) {
    res.push(value)
  }
  return res;
}
// 然后在调用时才指定T的具体类型
console.log(createArray<number>(5, 1));// [1,1,1,1,1]
console.log(createArray<string>(5, '1'));// ['1','1','1','1','1']
// 也可以不指定类型，ts会根据类型推论得到类型
console.log(createArray(5, '1'));// ['1','1','1','1','1']


// 多个泛型参数
// 如果一个函数有多个泛型参数，那就定义多个泛型类型
// 例如我们要实现一个交换两个变量的函数
function swap<T,U>(t: [T, U]): [U,T] {
  return [t[1], t[0]];
}
console.log(swap([1,'2'])); // ['2', 1]

// 泛型约束
// 泛型的类型是不确定的，只有到运行时才会确定下来，因此在函数中访问泛型参数的属性或方法，可能会报错。
// 泛型采用extends一个接口来约束泛型的类型，以保证在使用时不会报错
interface Length {
  length: number
}
function getLength<T extends Length>(value: T): number {
  return value.length; // T上面可能不存在length，因此需要泛型约束
}
// 泛型参数彼此之间也是可以相互约束的
function copyFields<T extends U, U>(target: T, source: U): T {
  for (let id in source) {
      target[id] = (<T>source)[id];
  }
  return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
copyFields(x, { b: 10, d: 20 });


// 泛型接口
interface createArrayFunc<T> {
  (length: number, value: T): Array<T>
}
// 注意在使用泛型接口时，需要指定泛型接口的类型。一般为any
let createArray2: createArrayFunc<string> = function<T> (length: number, value: T): Array<T> {
  let res: T[] = [];
  for(let i = 0; i < length; i++) {
    res.push(value)
  }
  return res;
}
console.log(createArray2(4, 'a')); // ['a', 'a', 'a', 'a']


// 泛型类
// 与泛型接口类似，泛型也可以用于定义类
class AddNumber<T> {
  value: T | undefined;
  add: ((x: T, y: T) => T) | undefined
}
let addNumber = new AddNumber<number>();
addNumber.value = 0;
addNumber.add = function(x: number, y: number) {
  return x + y;
}
console.log(addNumber.add(1,2));


// 泛型参数的默认类型
// 同函数的默认参数类似，泛型也可以设置默认类型，当泛型在使用时没有传入参数类型，并且无法通过类型推论得到类型的话，就会使用默认类型
function createArray3<T>(length: number, value: T): Array<T> {
  let res: T[] = [];
  for(let i = 0; i < length; i++) {
    res.push(value)
  }
  return res;
}

